//
//  method2_active.cpp
//  Simulation
//
//  Created by Christopher Kemsley on 3/4/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "EveryModel.hpp"

#include "EventMonitor.hpp"
#include "ModuleFactory.hpp"
#include "Transceiver.hpp"

#include "TransmissionBroker.hpp"
#include "BroadcastData.hpp"
#include "Log.hpp"

#include "SimulationParameters.hpp"

#include <stdlib.h>

using namespace Sim ;






struct	TransmittingModel_sensingSettings : public EveryModelSettings
{
	unsigned	lambdaScalar ;
	std::string	target ;
	
	
	TransmittingModel_sensingSettings	( std::string const & name )
	:	EveryModelSettings(name)
	,	lambdaScalar ( 1 )
		{
		add ( "lambdaScalar" , lambdaScalar ) ;
		add ( "target" , target ) ;
		}
	
	virtual void	validate ()
		{
		EveryModelSettings::validate() ;
		
		if ( target.size() != 1 )
			ThrowException ( "TransmittingModel_sensingSettings" , "Target length isn't 1" , __FILE__,__LINE__ ) ;
		}
} ;




struct	TransmittingModel_sensing : public EveryModel<TransmittingModel_sensingSettings> , public TransceiverDelegate
{
	TransmittingModel_sensing	( ModelVariant const & identity )
	:	EveryModel<TransmittingModel_sensingSettings>	( identity )
	,	mTransceiver ( TransmitterProfile(mConfig.location,mConfig.txRange) , mConfig.identifier[0] , *this )
	,	mState_(kStateIdle)
		{
		_backoffComplete () ;
		}
	
	virtual ~TransmittingModel_sensing ()
		{
		
		}
	
	
	
	
	/* Structure */
	
	Sim::Transceiver	mTransceiver ;
	
	char const		*	mState_ ;
	DP<Timer>			mReturnWaiter ;
	
	static char const * const	kStateIdle ;
	
	static char const * const	kStateWaitingForIdle ;
	static char const * const	kStatePerformingBackoff ;
	
	static char const * const	kStateSendingRTS ;
	static char const * const	kStateWaitingForCTS ;
	
	
	void		setState	( char const * inState )
		{
		Log::states("TransmittingModel_sensing") << mConfig.identifier << ": Changing state from " << mState_ << " to " << inState ;
		mState_ = inState ;
		}
	char const*	state () const
		{
		return mState_ ;
		}
	
	
	
	
	virtual void	lineDidBecomeBusy			()
		{
		Log::states("TransmittingModel_sensing") << "lineDidBecomeBusy" ;
		
		_cancelBackoff () ;
		}
	virtual void	lineDidBecomeOpen			()
		{
		Log::states("TransmittingModel_sensing") << "lineDidBecomeOpen" ;
		
		_performBackoff() ;
		}
	
	virtual void	connectionDidSucceed		()
		{
		Log::states("TransmittingModel_sensing") << "connectionDidSucceed" ;
		
		_performBackoff () ;
		}
	virtual void	connectionDidFail			()
		{
		Log::states("TransmittingModel_sensing") << "connectionDidFail" ;
		
		_performBackoff () ;
		}
	
	
	void	_performBackoff ()
		{
		Log::states("TransmittingModel_sensing") << "Starting to perform backoff" ;
		
		// Backoff for a "log" while
		Interval	delay	=	SimulationParameters::shared().randomBackoff(mConfig.lambdaScalar) ;
		Invocation * i = ObjectInvocation<TransmittingModel_sensing> :: invocation ( this , &TransmittingModel_sensing::_backoffComplete ) ;
		mReturnWaiter << new Timer ( delay , i ) ;
		}
	
	void	_backoffComplete ()
		{
		Log::states("TransmittingModel_sensing") << "Backoff complete. Starting TX sequence" ;
		
		mReturnWaiter << 0 ;
		
		if ( mTransceiver.isIdle() )
			{
			Length	length	=	SimulationParameters::shared().dataPacketSize ;
			Byte	buffer		[ length ] ;
			
			encodeBlock ( buffer,length , mConfig.identifier[0] , mConfig.target[0] ) ;
			
			mTransceiver . transmit ( buffer , length , mConfig.target[0] ) ;
			}
		else
			_performBackoff () ;
		}
	
	void	_cancelBackoff ()
		{
		mReturnWaiter << 0 ;
		}
	
	
	/*
	
	void	startTransmissionCycle ()
		{
		if ( receiverState() == Receiver::kReceiverStateIdle )
			{
			sendRTS () ;
			}
		else
			{
			startBackoff () ;
			}
		}
	
	
	void	startBackoff ()
		{
		setState ( kStateWaitingForIdle ) ;
		}
	virtual void	mediumDidBecomeIdle ()
		{
		if ( state() == kStateWaitingForIdle )
			{
			setState ( kStatePerformingBackoff ) ;
			
			_performBackoff () ;
			}
		else if ( state() == kStatePerformingBackoff )
			{
			mReturnWaiter -> setIsPaused(0) ;
			}
		}
	virtual void	mediumDidBecomeBusy ()
		{
		if ( state() == kStatePerformingBackoff )
			{
			mReturnWaiter -> setIsPaused(1) ;
			}
		}
	
	
	void	sendRTS ()
		{
		setState ( kStateSendingRTS ) ;
		
		disableReceiver() ;
		unsigned length = SimulationParameters::shared().controlPacketSize ;
		Broadcast	*	tx	=	beginTransmission ( mConfig.location , mConfig.txRange , Channel(1) , length ) ;
		encodeRTS ( tx->bytes , tx->length , mConfig.identifier[0] , mConfig.target[0] ) ;
		}
	virtual void	transmissionIsComplete ()
		{
		Log::states("TransmittingModel_sensing") << "Finished sending" ;
		
		if ( state() == kStateSendingRTS )
			{
			enableReceiver(mConfig.location) ;
			
			setState ( kStateWaitingForCTS ) ;
			
			// Setup a timeout
			Invocation * i	=	ObjectInvocation<TransmittingModel_sensing> :: invocation ( this , &TransmittingModel_sensing::_timeoutWaitingForResponse ) ;
			mReturnWaiter	=	new Timer ( SimulationParameters::shared().DIFSDuration , i ) ;
			}
		}
	
	
	void	_timeoutWaitingForResponse ()
		{
		Log::states("TransmittingModel_sensing") << "Timed out waiting for response" ;
		
		delete mReturnWaiter ;
		mReturnWaiter = 0 ;
		
		_performBackoff () ;
		}
	
	
	
	
	
	
	
	
	
	virtual void	handleReceivedBroadcast	( Sim::Broadcast * broadcast )
		{
		BroadcastInfo::Type		messageType ;
		DeviceID				sender ;
		DeviceID				target ;
		
		decodeBroadcast ( broadcast->bytes , messageType , sender , target ) ;
		
		if ( target == mConfig.identifier[0] )
			{
			
			}
		}
	
	
	*/
} ;





char const * const	TransmittingModel_sensing :: kStateIdle					=	"kStateIdle" ;
char const * const	TransmittingModel_sensing :: kStateWaitingForIdle		=	"kStateWaitingForIdle" ;
char const * const	TransmittingModel_sensing :: kStatePerformingBackoff	=	"kStatePerformingBackoff" ;
char const * const	TransmittingModel_sensing :: kStateSendingRTS			=	"kStateSendingRTS" ;
char const * const	TransmittingModel_sensing :: kStateWaitingForCTS		=	"kStateWaitingForCTS" ;



TypedModuleMaker<TransmittingModel_sensing> _TransmittingModel_sensing ( "csma_ca.sensing.active" ) ;
